package cadtoolbox.graphical;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.HashSet;
import java.util.Set;

import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;

import cadtoolbox.model.OligoGraph;

import edu.uci.ics.jung.algorithms.layout.CircleLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.BasicVisualizationServer;
import edu.uci.ics.jung.visualization.Layer;


public class MyVisualizationServer<V, E> extends BasicVisualizationServer<V, E> {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private GraphicInterfaceMouseAdapter mouse;
	private Layout<V,E> layout;
	private DataPanel data;
	
	public MyVisualizationServer(Layout<V, E> layout, GraphicInterfaceMouseAdapter mouse,DataPanel data) {
		super(layout);
		this.layout = layout;
		this.mouse = mouse;
		this.data = data;
	}

	public void paintComponent(Graphics g){
		Set<V> sel = ((OligoGraph<V,E>) layout.getGraph()).getSelected();
		
		// Update the selected nodes in case of multiselection
		if (mouse!=null && mouse.multiselect){
		Rectangle2D where = new Rectangle2D.Double(Math.min(mouse.start.x,mouse.now.x), Math.min(mouse.start.y,mouse.now.y), Math.abs(mouse.now.x-mouse.start.x), Math.abs(mouse.now.y-mouse.start.y));
		for (V v : layout.getGraph().getVertices()){
			// Vertex not selected anymore
			if (sel.contains(v) && !where.contains(layout.transform(v))){
				sel.remove(v);
				data.update();
				
			// Vertex newly selected	
			} else if (!sel.contains(v) && where.contains(layout.transform(v))){
				sel.add(v);
				data.update();
			}
		}
		
		// clicking somewhere
		} else if (mouse!=null && !mouse.draganddrop && mouse.now != null){
			this.clicDetected(sel);
			data.update();
			mouse.now = null;
			
		// drag and drop	
		} else if (mouse != null && mouse.draganddrop){
			Point2D p = layout.transform((V) mouse.dragged);
			p = this.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p);
			Point2D current;
			if (sel.contains(mouse.dragged)){
				for(V movingv : sel){
					current = layout.transform(movingv);
					current.setLocation(current.getX()+(mouse.now.getX() - p.getX()),current.getY()+(mouse.now.getY() - p.getY()) );
					layout.setLocation(movingv, current);
				}
			} else {
			layout.setLocation((V) mouse.dragged, mouse.now);
			}
		}
		
		super.paintComponent(g);
		
		// draw the selection rectangle
		if (mouse!=null && mouse.multiselect){
		g.setColor(getForeground());
		g.drawRect(Math.min(mouse.start.x,mouse.now.x), Math.min(mouse.start.y,mouse.now.y), Math.abs(mouse.now.x-mouse.start.x), Math.abs(mouse.now.y-mouse.start.y));
		}
		
	}
	
	public Layout getJUNGLayout(){
		return this.layout;
	}

	private void clicDetected (Set<V> sel){
		Point2D p;
		AffineTransform affine;
		V possibleEdgeOrigin = null;
		
		if(!mouse.edit){
			if(sel.size() == 1){
				possibleEdgeOrigin = sel.iterator().next();
			}
			sel.clear();
		}
		for(V v: layout.getGraph().getVertices()){
			p = layout.transform(v);
			p = this.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p);
			affine = AffineTransform.getTranslateInstance(p.getX(), p.getY());
			if (affine.createTransformedShape(this.getRenderContext().getVertexShapeTransformer().transform(v)).contains(mouse.now))
			{
				if (mouse.edit && sel.contains(v)){
					sel.remove(v);
				
				} else if (!mouse.edit && possibleEdgeOrigin !=null && !((OligoGraph<V,E>)layout.getGraph()).isInhibitor(possibleEdgeOrigin)){
					E newedge = ((OligoGraph<V,E>)layout.getGraph()).getEdgeFactory().createEdge(possibleEdgeOrigin, v);
					if (!layout.getGraph().containsEdge(newedge)){
						((OligoGraph<V,E>)layout.getGraph()).addActivation(newedge, possibleEdgeOrigin, v);
					}
				} else if (!mouse.edit && possibleEdgeOrigin !=null && ((OligoGraph<V,E>)layout.getGraph()).isInhibitor(possibleEdgeOrigin)){
					JOptionPane.showMessageDialog(this.getParent(), "Inhibiting sequences can't be used as activator");
				} else{
					sel.add(v);
				}
			}
		}
	}

	public void rightclic(Point point) {
		Point2D p;
		AffineTransform affine;
		JPopupMenu pm = new JPopupMenu();
		for (V v: layout.getGraph().getVertices()){
			p = layout.transform(v);
			p = this.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p);
			affine = AffineTransform.getTranslateInstance(p.getX(), p.getY());
			if (affine.createTransformedShape(this.getRenderContext().getVertexShapeTransformer().transform(v)).contains(point))
			{
				
				JLabel title = new JLabel(""+v);
				pm.add(title);
				pm.add(new JSeparator());
				JMenuItem menuitem = new JMenuItem("Delete");
				menuitem.setMnemonic(KeyEvent.VK_D);
				menuitem.getAccessibleContext().setAccessibleDescription(
			        "Delete Sequence");
				menuitem.setActionCommand("delete");
				final V selected = v;
				menuitem.addActionListener(new ActionListener(){
		        	public void actionPerformed(ActionEvent e){
		        		OligoGraph<V,E> graph = ((OligoGraph<V,E>) layout.getGraph());
		        		graph.getSelected().add(selected);
		        		for (V i: graph.getSelected()){
		        			if(graph.getVertices().contains(i))
		        				graph.removeVertex(i);
		        			 
		        		 }
		        		graph.getSelected().clear();
		        		 data.update();
		        		 data.getParent().repaint();
		        		 graph.replot(); //reploted once
		        	}
				});
				pm.add(menuitem);
				menuitem = new JMenuItem("Add input");
				menuitem.setActionCommand("addinput");
				menuitem.addActionListener(new ActionListener(){
		        	public void actionPerformed(ActionEvent e){
		        		InputManageWindow<V> imw = new InputManageWindow<V>(selected,(OligoGraph<V,E>)layout.getGraph());
		        		imw.setVisible(true);
		        		imw.setAlwaysOnTop(true);
		        	}
				});
				pm.add(menuitem);
				pm.show(this,(int) Math.round(point.getX()),(int) Math.round(point.getY()));
				break;
			}
		}
		
	}
	
}
